This is the pupil preprocessing file when you have data from both eyes.

The same basic workflow is used here, as was detailed in the preprocess.Rmd file, which focussed on preprocessing data for one eye. Therefore, I will not repeat any details here. For a description of the basic workflow and functions, see the preprocess.Rmd file. Here, I’ll just show you the steps that you might take to preprocess and combine pupil data for both eyes.

1. load packages and custom functions plus adjust settings

packages

pkg <- c("pupillometry", "tidyverse", "here", "patchwork", "future", "RColorBrewer",
         "parallel") 

lapply(pkg, library, character.only = TRUE)

custom functions

source(here("custom_functions", "preprocess_both.r"))
source(here("custom_functions", "parameter_tests_both.r"))  
source(here("custom_functions", "settings.r"))

check multicore settings

plan(multicore)
message(paste("Supporting multicore:", supportsMulticore()))
## Supporting multicore: TRUE
message(paste("Available cores:", detectCores()))
## Available cores: 8

2. read in the data

take a quick look at the simulated data

simulated pupil data for both eyes across 3 trials and 3 participants

simulated pupil data for both eyes across 3 trials and 3 participants

notes on the pupil_read() function for both eyes

Some extra arguments are required when you have data for both eyes, as you have to specify pupil size plus x and y coordinates for the left and right eye separately.

And if you leave eye_use as the default (NULL), then it will expect data for both eyes to be present.

read data and add required columns

raw_data_cols <- read_csv("data/sim/sim_pupil_data_both.csv", 
                          n_max = 0) %>%
  colnames()

# Find columns to include
already_specified <- c("time", "pupil", "subject", "trial")
cols_to_include <- setdiff(raw_data_cols, already_specified)

## read in
sim_pupil_data_both <- pupil_read(
  file = here("data", "sim", "sim_pupil_data_both.csv"),
  eyetracker = "",
  eye_use = NULL, ## NULL is the default and it keeps both eyes
  time = "time",
  left_pupil.mm = "pupil_left",
  right_pupil.mm = "pupil_right",
  left_gaze.x = "x_pos_left",
  left_gaze.y = "y_pos_left",
  right_gaze.x = "x_pos_right",
  right_gaze.y = "y_pos_right",
  subject = "subject",
  trial = "trial",
  delim = ",",
  message_event = "message",  ## event info stored in this column
  include_col = cols_to_include
)
head(sim_pupil_data_both)
## # A tibble: 6 × 11
##   Subject Trial  Time Stimulus L_Pupil_Diameter.mm R_Pupil_Diameter.mm
##     <dbl> <dbl> <dbl> <chr>                  <dbl>               <dbl>
## 1       1     1     0 fixation                2.94                2.94
## 2       1     1    17 fixation                3.03                3.03
## 3       1     1    33 fixation                3.26                3.27
## 4       1     1    50 fixation                3.16                3.21
## 5       1     1    67 fixation                3.22                3.22
## 6       1     1    83 fixation                3.43                3.45
## # ℹ 5 more variables: L_Gaze_Position.x <dbl>, L_Gaze_Position.y <dbl>,
## #   R_Gaze_Position.x <dbl>, R_Gaze_Position.y <dbl>, Pupils.r <dbl>

This gives a warning about parsing issues, but the column types seem correct to me. So I think this can be safely ignored. I leave the warning in just for folks who might use other datasets where it could be helpful.

3. preprocess

I created a new function to apply preprocessing to both eyes and then combine them at some point. preprocess_and_visualize_both()

set parameters

custom_params <- list(
  deblink = list(extend = 75),
  artifact = list(n = 8),
  missing = list(missing_allowed = .90),
  upsample = list(),
  smooth = list(type = "hann", n = 50),
  interpolate = list(type = "cubic-spline", maxgap = 1000, hz = 1000),
  baseline = list(bc_onset_message = "target", baseline_duration = 1000, 
                  type = "subtractive")
)

run preprocessing

results_both <- preprocess_and_visualize_both(
  pupil_data = sim_pupil_data_both,
  plot_dir = here("figures", "preprocessing", "both_eyes", "sim"),
  output_dir = here("data", "preprocessing", "both_eyes", "sim"),
  params = custom_params,
  save_intermediate_data = FALSE,
  save_individual_plots = FALSE,
  return_all_steps = TRUE,
  bin_length = 200,
  smooth_then_interp = TRUE,
  units = "mm"
)

preprocessing output

By default, the preprocessing script produces a bunch of figures and data, as follows:

In the plot directory, it produces:

  • a subdirectory per participant
  • a plot per trial, which summarises the preprocesing steps with one panel per processing step.

An example of subject 1, trial 3 is shown below.

example preprocessing summary plot for both eyes and 1 trial

example preprocessing summary plot for both eyes and 1 trial

In the data directory, it produces:

  • a processed data file for each subject and a combined file for all subjects
  • a binned data file for each subject and a combined file for all subjects

4. parameter tests

smooth_combinations <- list(
  list(type = "mwa", n = 10),
  list(type = "mwa", n = 50),
  list(type = "mwa", n = 100),
  list(type = "mwa", n = 200)
)

smooth_results_both <- test_parameter_combinations_both(
  step_data = results_both,  # From your preprocessing
  param_combinations = smooth_combinations,
  processing_function = "pupil_smooth",
  input_step = "upsample_data",  # Step to use as input
  plot_dir = here("figures", "parameter_tests", "both_eyes", "sim", "smooth"),
  output_dir = here("data", "parameter_tests", "both_eyes", "sim", "smooth"),
  save_param_data = FALSE
)

parameter test output

An example of subject 1, trial 3 is shown below.

example parameter tests plot for both eyes and 1 trial

example parameter tests plot for both eyes and 1 trial

5. test arbitrary units with both eyes

create the data

read in data and create pretend left and right data (i.e., duplicate the existing data just to test that the preprocessing pipeline works with two eyes and arbitrary units).

gazer_data <- read_csv("data/gazer/gazer_data.csv")
# head(gazer_data)

duplicate

gazer_data_both <- gazer_data %>% 
  rename(pupil_left = pupil, x_pos_left = x, y_pos_left = y) %>% 
  mutate(pupil_right = pupil_left + 20, x_pos_right = x_pos_left + 20, y_pos_right = y_pos_left) 
head(gazer_data_both)
## # A tibble: 6 × 17
##   subject trial  time pupil_left x_pos_left y_pos_left blink message   acc block
##     <dbl> <dbl> <dbl>      <dbl>      <dbl>      <dbl> <dbl> <chr>   <dbl> <dbl>
## 1      11     1     0       3635       972.       563.     0 MODERE…     1     0
## 2      11     1     4       3634       973.       565.     0 <NA>        1     0
## 3      11     1     8       3626       972.       567.     0 <NA>        1     0
## 4      11     1    12       3622       971        566.     0 <NA>        1     0
## 5      11     1    16       3621       972.       566.     0 <NA>        1     0
## 6      11     1    20       3621       972.       566.     0 <NA>        1     0
## # ℹ 7 more variables: rt <dbl>, item <chr>, script <chr>, alt <chr>,
## #   pupil_right <dbl>, x_pos_right <dbl>, y_pos_right <dbl>
write_csv(gazer_data_both, "data/gazer/gazer_data_both.csv")

read data with pupil_read() and add required columns

gazer_data_cols <- read_csv("data/gazer/gazer_data_both.csv", 
                          n_max = 0) %>%
  colnames()

# Find columns to include
already_specified <- c("time", "pupil", "subject", "trial")
cols_to_include <- setdiff(raw_data_cols, already_specified)

## read in
gazer_pupil_data_both <- pupil_read(
  file = here("data", "gazer", "gazer_data_both.csv"),
  eyetracker = "",
  eye_use = NULL, ## NULL is the default and it keeps both eyes
  time = "time",
  left_pupil.px = "pupil_left",
  right_pupil.px = "pupil_right",
  left_gaze.x = "x_pos_left",
  left_gaze.y = "y_pos_left",
  right_gaze.x = "x_pos_right",
  right_gaze.y = "y_pos_right",
  subject = "subject",
  trial = "trial",
  delim = ",",
  message_event = "message",  ## event info stored in this column
  include_col = cols_to_include
)
head(gazer_pupil_data_both)
## # A tibble: 6 × 11
##   Subject Trial  Time Stimulus      L_Pupil_Diameter.px R_Pupil_Diameter.px
##     <dbl> <dbl> <dbl> <chr>                       <dbl>               <dbl>
## 1      11     1     0 MODERECORDCRL                3635                3655
## 2      11     1     4 MODERECORDCRL                3634                3654
## 3      11     1     8 MODERECORDCRL                3626                3646
## 4      11     1    12 MODERECORDCRL                3622                3642
## 5      11     1    16 MODERECORDCRL                3621                3641
## 6      11     1    20 MODERECORDCRL                3621                3641
## # ℹ 5 more variables: L_Gaze_Position.x <dbl>, L_Gaze_Position.y <dbl>,
## #   R_Gaze_Position.x <dbl>, R_Gaze_Position.y <dbl>, Pupils.r <dbl>

set parameters

custom_params <- list(
  deblink = list(extend = 75),
  artifact = list(n = 8),
  missing = list(missing_allowed = .90),
  upsample = list(),
  smooth = list(type = "hann", n = 50),
  interpolate = list(type = "cubic-spline", maxgap = 500, hz = 1000),
  baseline = list(bc_onset_message = "target", baseline_duration = 100, 
                  type = "subtractive")
)

run preprocessing

results_both_au <- preprocess_and_visualize_both(
  pupil_data = gazer_pupil_data_both,
  plot_dir = here("figures", "preprocessing", "both_eyes", "real"),
  output_dir = here("data", "preprocessing", "both_eyes", "real"),
  params = custom_params,
  save_intermediate_data = FALSE,
  save_individual_plots = FALSE,
  return_all_steps = TRUE,
  bin_length = 200,
  smooth_then_interp = TRUE,
  units = "px"
)

preprocessing output

example preprocessing summary plot for both eyes and 1 trial with arbitrary units

example preprocessing summary plot for both eyes and 1 trial with arbitrary units